package tech.xs.sys.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.xs.auth.api.AuthPathRoleApi;
import tech.xs.common.domain.enums.YesNoEnum;
import tech.xs.common.framework.domain.constant.Symbol;
import tech.xs.common.framework.domain.model.ApiResult;
import tech.xs.common.framework.service.CrudServiceImpl;
import tech.xs.common.framework.util.Assert;
import tech.xs.common.framework.util.RoleUtil;
import tech.xs.common.sys.domain.constant.SysCacheKeyConstant;
import tech.xs.sys.dao.SysRoleDao;
import tech.xs.sys.dao.SysRolePermissionDao;
import tech.xs.sys.dao.SysUserRoleDao;
import tech.xs.sys.dao.SysWebMenuShowDao;
import tech.xs.sys.domain.entity.SysRole;
import tech.xs.sys.domain.entity.SysRolePermission;
import tech.xs.sys.domain.entity.SysUserRole;
import tech.xs.sys.domain.entity.SysWebMenuShow;
import tech.xs.sys.domain.enums.SysWebMenuShowType;
import tech.xs.sys.service.SysRolePermissionService;
import tech.xs.sys.service.SysRoleService;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class SysRoleServiceImpl extends CrudServiceImpl<SysRoleDao, SysRole> implements SysRoleService {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private SysRoleDao sysRoleDao;
    @Resource
    private SysRolePermissionDao sysRolePermissionDao;
    @Resource
    private SysUserRoleDao sysUserRoleDao;
    @Resource
    private AuthPathRoleApi authPathRoleApi;
    @Resource
    private SysRolePermissionService sysRolePermissionService;
    @Resource
    private SysWebMenuShowDao sysWebMenuShowDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRoleByCode(String roleCode) {
        Long userRoleCount = sysUserRoleDao.selectCount(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getRoleCode, roleCode));
        Assert.isTrue(userRoleCount <= 0, "角色已经被使用");
        sysRoleDao.delete(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getCode, roleCode));
        sysRolePermissionDao.delete(Wrappers.<SysRolePermission>lambdaQuery().eq(SysRolePermission::getRoleCode, roleCode));
        sysRolePermissionService.deleteEnableRolePermissionCache();
        deleteEnableRoleCache();
    }

    @Override
    public Set<String> loadEnableRoleCache(Set<String> roles) {
        if (roles == null || roles.isEmpty()) {
            return new HashSet<>();
        }
        String cacheKey = SysCacheKeyConstant.SYS_ENABLE_ROLE_CACHE + RoleUtil.getRolesHashCode(roles);
        RLock lock = RoleUtil.getEnableRoleCacheWriteLock();
        try {
            RBucket<Set<String>> bucket = redissonClient.getBucket(cacheKey);
            List<SysRole> sysRoles = sysRoleDao.selectList(Wrappers.<SysRole>lambdaQuery()
                    .eq(SysRole::getRoleStatus, YesNoEnum.YES)
                    .in(SysRole::getCode, roles)
            );
            Set<String> cacheRoles = new HashSet<>();
            for (SysRole item : sysRoles) {
                cacheRoles.add(item.getCode());
            }
            bucket.set(cacheRoles);
            return cacheRoles;
        } catch (Exception e) {
            log.error("获取启用角色失败[缓存不存在]", e);
            throw e;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void deleteEnableRoleCache() {
        RLock lock = RoleUtil.getEnableRoleCacheWriteLock();
        try {
            redissonClient.getKeys().deleteByPattern(SysCacheKeyConstant.SYS_ENABLE_ROLE_CACHE + Symbol.ASTERISK);
        } catch (Exception e) {
            log.error("删除启用角色失败", e);
            throw e;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean isUsed(String roleCode) {
        if (sysRolePermissionDao.selectCount(Wrappers.<SysRolePermission>lambdaQuery().eq(SysRolePermission::getRoleCode, roleCode)) > 0) {
            log.info("角色已经被使用 角色已经绑定权限 roleCode:" + roleCode);
            return true;
        }
        if (sysUserRoleDao.selectCount(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getRoleCode, roleCode)) > 0) {
            log.info("角色已经被使用 角色已经绑定用户 roleCode:" + roleCode);
            return true;
        }
        if (sysWebMenuShowDao.selectCount(Wrappers.<SysWebMenuShow>lambdaQuery()
                .eq(SysWebMenuShow::getRoleCode, roleCode)
                .eq(SysWebMenuShow::getShowType, SysWebMenuShowType.ROLE)) >= 0) {
            log.info("角色已经被使用 角色已经绑定菜单 roleCode:" + roleCode);
            return true;
        }
        ApiResult<Boolean> authResp = authPathRoleApi.isUsedRoleCode(roleCode);
        return authResp.getData();
    }


}
